package com.seednia.module.core.xport;

import java.beans.PropertyDescriptor;
import java.io.BufferedWriter;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Date;
import java.util.Map;

import javax.xml.bind.JAXBException;
import javax.xml.bind.annotation.XmlTransient;

import org.apache.commons.beanutils.PropertyUtils;

import au.com.bytecode.opencsv.CSVWriter;

import com.seednia.foundation.framework.dto.AbstractRecord;
import com.seednia.foundation.framework.service.IDataOrientedService;
import com.seednia.foundation.framework.service.ServiceContainer;

/**
 * 
 * @author Young Suk Ahn
 *
 */
public class ExporterCsv extends AbstractExporter  {
	
	public static final String FORMAT = "csv";
	
	public static final String NULL_STRING = "__NULL__";
	
	protected SimpleDateFormat sdf = new SimpleDateFormat("yyyy/MM/dd HH:mm:ss");
	
	public ExporterCsv()
	{
		super.setFormat(FORMAT);
	}


	/**
	 * Jaxb Marshalling
	 * @param objToExport
	 * @param os
	 * @throws JAXBException
	 */
	public void exportTo(XportDataContainer objToExport, OutputStream os, Map<String, Object> params) throws Exception
	{
		BufferedWriter writer = new BufferedWriter(new OutputStreamWriter(os));
		CSVWriter csvWriter = new CSVWriter(writer);
		
		String processorServiceId = (String)params.get(AbstractXporterBase.PNAME_PROCESSOR_SERVICE_ID);
		IDataOrientedService dos = (IDataOrientedService)ServiceContainer.getInstance().get(processorServiceId);

		String metaHeader[] = {AbstractXporterBase.PNAME_PROCESSOR_SERVICE_ID, "exportDate","comments"};
		String metaData[] = {processorServiceId,stringFromObject(objToExport.getExportDate()),objToExport.getComments()};
		
		csvWriter.writeNext(metaHeader);
		csvWriter.writeNext(metaData);
		
		// Writes the header
		String header[] = this.obtainHeader(dos);
		csvWriter.writeNext(header);
		
		Collection<AbstractRecord> records = null;
	    for (XportData xporData: objToExport.getXportDataList()) {
	    	if (xporData.getProcessorServiceId().equals(processorServiceId)) {
	    		records = xporData.getRecords();
	    		break;
	    	}
	    }
		
	    if (records != null) {
		    for (AbstractRecord record: records) {
		    	String line[] = processLineOut(header, record);
		    	csvWriter.writeNext(line);
		    }
	    }
	    writer.flush();
	}
	
	
	/**
	 * returns an array of string this is a list of property names that are not transient
	 * @param record
	 * @return
	 * @throws IllegalStateException
	 * @throws InstantiationException
	 * @throws IllegalAccessException
	 * @throws InvocationTargetException
	 * @throws NoSuchMethodException
	 */
	protected String[] obtainHeader(IDataOrientedService dos) throws IllegalStateException, InstantiationException, IllegalAccessException, InvocationTargetException, NoSuchMethodException
	{
		ArrayList<String> header = new ArrayList<String>() ;
		PropertyDescriptor propDescrs[] = PropertyUtils.getPropertyDescriptors(dos.getRecordClass());
		for (PropertyDescriptor propDescr: propDescrs) {
			Method writeMethod = propDescr.getWriteMethod();
			if (writeMethod != null) {
				XmlTransient transientAnnot = writeMethod.getAnnotation(XmlTransient.class);
				if (transientAnnot == null) {
					header.add(propDescr.getDisplayName());
				}
			}
		}
		return header.toArray(new String[header.size()]);
	}
	
	protected String[] processLineOut(String[] header, AbstractRecord record) throws IllegalStateException, InstantiationException, IllegalAccessException, InvocationTargetException, NoSuchMethodException
	{
		String line[] = new String[header.length];
		int index = 0;
		for (String propName: header) {
			line[index++] = stringFromObject(PropertyUtils.getProperty(record, propName));
		}
		return line;
	}
	
	
	
	protected String stringFromObject(Object val)
	{
		if (val == null)
			return NULL_STRING;
		String retval = val.toString();
		if (val instanceof Date) {
			retval = sdf.format(val);
		}
		return retval;
	}
}
